{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Jigsaw in IPython\n",
    "\n",
    "This notebook demonstrates using the %jigsaw magic in IPython.\n",
    "\n",
    "Dependencies:\n",
    "\n",
    "1. system running IPython or Jupyter\n",
    "2. [metakernel](https://github.com/Calysto/metakernel)\n",
    "\n",
    "## Overview\n",
    "\n",
    "Jigsaw is a combination of Google's Blockly drag-and-drop interface in the browser with Python. With Jigsaw, you can:\n",
    "\n",
    "* do many Python tasks in the block-only view\n",
    "* have access to predefined Python variables\n",
    "* create functions and variables in Jigsaw, but use them in Python cells\n",
    "\n",
    "To use in IPython, you need to add the metakernel magics for IPython:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from metakernel import register_ipython_magics\n",
    "register_ipython_magics()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, let's define some Python things:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x = 42"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def myfunc(a, b):\n",
    "    return a + b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we create a Jigsaw workspace named \"workspace1\". This will create two files in this subdirectory:\n",
    "\n",
    "1. workspace1.html\n",
    "2. workspace1.xml\n",
    "\n",
    "Once you have the Jigsaw workspace showing, then you can start dragging blocks around:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "\n",
       "    if (document.jigsaw_register_workspace === undefined) {\n",
       "\n",
       "        document.jigsaw_workspaces = {};\n",
       "\n",
       "        document.jigsaw_register_workspace = function(workspace_filename, workspace, xml_init) {\n",
       "            workspace.element = document.element;\n",
       "            document.jigsaw_workspaces[workspace_filename] = workspace;\n",
       "\n",
       "            try {\n",
       "                $([window.parent.IPython.events]).on('notebook_saved.Notebook', function() { \n",
       "                    try {\n",
       "                        document.jigsaw_save_workspace(workspace_filename); \n",
       "                    } catch(err) {\n",
       "                        // ignore failure, might not exist\n",
       "                    }\n",
       "                });\n",
       "            } catch (err) {\n",
       "                // rendering for display\n",
       "            }\n",
       "            \n",
       "            var xml = document.jigsaw_loadXMLDoc(workspace_filename);\n",
       "            if (xml === null) {\n",
       "                xml = xml_init;\n",
       "                if (xml === null) {\n",
       "                    xml = Blockly.Xml.textToDom('<xml id=\"workspace\"></xml>');\n",
       "                }\n",
       "            } else {\n",
       "                xml = xml.children[0]; // document\n",
       "            }\n",
       "            Blockly.Xml.domToWorkspace(workspace, xml);\n",
       "        };\n",
       "\n",
       "        document.jigsaw_handle_output = function(workspace_filename, out) {\n",
       "            var workspace = document.jigsaw_workspaces[workspace_filename];\n",
       "            //var output_area = workspace.element.output_area;\n",
       "            var cell_index = document.jigsaw_get_cell(workspace.element);\n",
       "            var cell = IPython.notebook.get_cell(cell_index);\n",
       "            var res = null;\n",
       "            var data = null;\n",
       "            document.cell = cell;\n",
       "            document.out = out;\n",
       "            if (out.msg_type == \"stream\") {\n",
       "                res = out.content.text;\n",
       "                //document.getElementById('code_output').value += res.toString();\n",
       "            } else if (out.msg_type === \"pyout\") {\n",
       "                // if output is a python object\n",
       "                res = out.content.data[\"text/plain\"];\n",
       "                //document.getElementById('code_output').value += res.toString(); \n",
       "            } else if (out.msg_type == \"pyerr\") {\n",
       "                // if output is a python error\n",
       "                res = out.content.data[\"text/plain\"];\n",
       "                //document.getElementById('code_output').value += res.toString();\n",
       "            } else if (out.msg_type == \"execute_result\") {\n",
       "                var str = out.content.data[\"text/plain\"];\n",
       "                res = str;\n",
       "                if (res.indexOf(\"u\") == 0)\n",
       "                    res = res.substring(2, res.length - 1) + \"\\n\";\n",
       "                if (res) {\n",
       "                    //document.getElementById('code_output').value += res.toString();\n",
       "                }\n",
       "            } else if (out.msg_type == \"error\") {\n",
       "                res = out.content.ename + \": \" + out.content.evalue + \"\\n\";\n",
       "                // FIXME: out.traceback is Array of terminal color-coded [-codes\n",
       "            } else {\n",
       "                // if output is something we haven't thought of\n",
       "                res = out.toString();\n",
       "                //document.getElementById('code_output').value += res.toString();\n",
       "            }\n",
       "            if (res) {\n",
       "                cell.output_area.append_output({output_type: \"stream\", text: res.toString(), name: \"output\"});\n",
       "            }\n",
       "        };\n",
       "        \n",
       "        document.jigsaw_generate = function(workspace_filename, language, insert_code) {\n",
       "            var workspace = document.jigsaw_workspaces[workspace_filename];\n",
       "            var callbacks = { 'iopub' : {'output' : function(out) { document.jigsaw_handle_output(workspace_filename, out); }}};\n",
       "            var code = Blockly[language].workspaceToCode(workspace);\n",
       "            if (insert_code == 1) {\n",
       "                var cell_index = document.jigsaw_get_cell(workspace.element);\n",
       "                var cell = IPython.notebook.insert_cell_at_index(0, cell_index + 1);\n",
       "                cell.set_text(code);\n",
       "            } else {\n",
       "                window.parent.IPython.notebook.kernel.execute(code,\n",
       "                                                              callbacks,\n",
       "                                                              {silent: false});\n",
       "            }\n",
       "        };\n",
       "        \n",
       "        document.jigsaw_save_workspace = function(workspace_filename) {\n",
       "            var workspace = document.jigsaw_workspaces[workspace_filename];\n",
       "            var xml = Blockly.Xml.workspaceToDom(workspace);\n",
       "            document.xml = xml;\n",
       "            if (xml !== undefined) {\n",
       "                console.log(xml);\n",
       "                //xml.style = \"display: none\";\n",
       "                //xml.id = \"workspace\";\n",
       "                var xml_text = Blockly.Xml.domToText(xml)\n",
       "                IPython.notebook.kernel.execute('%%file ' + workspace_filename + '\\n' + xml_text);\n",
       "            }\n",
       "        };\n",
       "        \n",
       "        document.jigsaw_loadXMLDoc = function(filename) {\n",
       "            var xhttp = new XMLHttpRequest();\n",
       "            xhttp.open(\"GET\", filename, false);\n",
       "            xhttp.send();\n",
       "            return xhttp.responseXML;\n",
       "        };\n",
       "    }\n",
       "\n",
       "    document.jigsaw_get_cell = function (element) {\n",
       "        // FIXME: brittle and ugly:\n",
       "        var mydiv = element[0].parentNode.parentNode.parentNode.parentNode;\n",
       "        var cells = IPython.notebook.get_cells();\n",
       "        for (var i = 0; i < cells.length; i++) {\n",
       "            if (mydiv === cells[i].element[0]) {\n",
       "                return i;\n",
       "            }\n",
       "        }\n",
       "        return null;\n",
       "    };\n",
       "\n",
       "    document.jigsaw_clear_output = function (workspace_filename) {\n",
       "        var workspace = document.jigsaw_workspaces[workspace_filename];\n",
       "        var cell_index = document.jigsaw_get_cell(workspace.element);\n",
       "        var cell = IPython.notebook.get_cell(cell_index);\n",
       "        // FIXME: brittle and ugly:\n",
       "        cell.element[0].children[2].children[1].children[2].children[1].children[0].innerHTML = \"\"\n",
       "        cell.output_area.outputs[2].text = \"\"\n",
       "    };\n",
       "\n",
       "    try {\n",
       "        document.element = element;\n",
       "    } catch (err) {\n",
       "        // rendering\n",
       "    }\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<iframe src=\"workspace1.html\" width=\"100%\" height=\"350\" style=\"resize: both; overflow: auto;\"></iframe>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "output",
     "output_type": "stream",
     "text": [
      "42\n"
     ]
    }
   ],
   "source": [
    "%jigsaw Python --workspace workspace1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"workspace1.png\"></img>\n",
    "\n",
    "In the above example, I pulled a print block from the Text category on the left, and a variable block from the Variables category. Snapping them together forms a valid program. I then clicked on the \"Run\" button to run the code. x got its value from the Python cell above.\n",
    "\n",
    "Let's create another workspace:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "\n",
       "    if (document.jigsaw_register_workspace === undefined) {\n",
       "\n",
       "        document.jigsaw_workspaces = {};\n",
       "\n",
       "        document.jigsaw_register_workspace = function(workspace_filename, workspace, xml_init) {\n",
       "            workspace.element = document.element;\n",
       "            document.jigsaw_workspaces[workspace_filename] = workspace;\n",
       "\n",
       "            try {\n",
       "                $([window.parent.IPython.events]).on('notebook_saved.Notebook', function() { \n",
       "                    try {\n",
       "                        document.jigsaw_save_workspace(workspace_filename); \n",
       "                    } catch(err) {\n",
       "                        // ignore failure, might not exist\n",
       "                    }\n",
       "                });\n",
       "            } catch (err) {\n",
       "                // rendering for display\n",
       "            }\n",
       "            \n",
       "            var xml = document.jigsaw_loadXMLDoc(workspace_filename);\n",
       "            if (xml === null) {\n",
       "                xml = xml_init;\n",
       "                if (xml === null) {\n",
       "                    xml = Blockly.Xml.textToDom('<xml id=\"workspace\"></xml>');\n",
       "                }\n",
       "            } else {\n",
       "                xml = xml.children[0]; // document\n",
       "            }\n",
       "            Blockly.Xml.domToWorkspace(workspace, xml);\n",
       "        };\n",
       "\n",
       "        document.jigsaw_handle_output = function(workspace_filename, out) {\n",
       "            var workspace = document.jigsaw_workspaces[workspace_filename];\n",
       "            //var output_area = workspace.element.output_area;\n",
       "            var cell_index = document.jigsaw_get_cell(workspace.element);\n",
       "            var cell = IPython.notebook.get_cell(cell_index);\n",
       "            var res = null;\n",
       "            var data = null;\n",
       "            document.cell = cell;\n",
       "            document.out = out;\n",
       "            if (out.msg_type == \"stream\") {\n",
       "                res = out.content.text;\n",
       "                //document.getElementById('code_output').value += res.toString();\n",
       "            } else if (out.msg_type === \"pyout\") {\n",
       "                // if output is a python object\n",
       "                res = out.content.data[\"text/plain\"];\n",
       "                //document.getElementById('code_output').value += res.toString(); \n",
       "            } else if (out.msg_type == \"pyerr\") {\n",
       "                // if output is a python error\n",
       "                res = out.content.data[\"text/plain\"];\n",
       "                //document.getElementById('code_output').value += res.toString();\n",
       "            } else if (out.msg_type == \"execute_result\") {\n",
       "                var str = out.content.data[\"text/plain\"];\n",
       "                res = str;\n",
       "                if (res.indexOf(\"u\") == 0)\n",
       "                    res = res.substring(2, res.length - 1) + \"\\n\";\n",
       "                if (res) {\n",
       "                    //document.getElementById('code_output').value += res.toString();\n",
       "                }\n",
       "            } else if (out.msg_type == \"error\") {\n",
       "                res = out.content.ename + \": \" + out.content.evalue + \"\\n\";\n",
       "                // FIXME: out.traceback is Array of terminal color-coded [-codes\n",
       "            } else {\n",
       "                // if output is something we haven't thought of\n",
       "                res = out.toString();\n",
       "                //document.getElementById('code_output').value += res.toString();\n",
       "            }\n",
       "            if (res) {\n",
       "                cell.output_area.append_output({output_type: \"stream\", text: res.toString(), name: \"output\"});\n",
       "            }\n",
       "        };\n",
       "        \n",
       "        document.jigsaw_generate = function(workspace_filename, language, insert_code) {\n",
       "            var workspace = document.jigsaw_workspaces[workspace_filename];\n",
       "            var callbacks = { 'iopub' : {'output' : function(out) { document.jigsaw_handle_output(workspace_filename, out); }}};\n",
       "            var code = Blockly[language].workspaceToCode(workspace);\n",
       "            if (insert_code == 1) {\n",
       "                var cell_index = document.jigsaw_get_cell(workspace.element);\n",
       "                var cell = IPython.notebook.insert_cell_at_index(0, cell_index + 1);\n",
       "                cell.set_text(code);\n",
       "            } else {\n",
       "                window.parent.IPython.notebook.kernel.execute(code,\n",
       "                                                              callbacks,\n",
       "                                                              {silent: false});\n",
       "            }\n",
       "        };\n",
       "        \n",
       "        document.jigsaw_save_workspace = function(workspace_filename) {\n",
       "            var workspace = document.jigsaw_workspaces[workspace_filename];\n",
       "            var xml = Blockly.Xml.workspaceToDom(workspace);\n",
       "            document.xml = xml;\n",
       "            if (xml !== undefined) {\n",
       "                console.log(xml);\n",
       "                //xml.style = \"display: none\";\n",
       "                //xml.id = \"workspace\";\n",
       "                var xml_text = Blockly.Xml.domToText(xml)\n",
       "                IPython.notebook.kernel.execute('%%file ' + workspace_filename + '\\n' + xml_text);\n",
       "            }\n",
       "        };\n",
       "        \n",
       "        document.jigsaw_loadXMLDoc = function(filename) {\n",
       "            var xhttp = new XMLHttpRequest();\n",
       "            xhttp.open(\"GET\", filename, false);\n",
       "            xhttp.send();\n",
       "            return xhttp.responseXML;\n",
       "        };\n",
       "    }\n",
       "\n",
       "    document.jigsaw_get_cell = function (element) {\n",
       "        // FIXME: brittle and ugly:\n",
       "        var mydiv = element[0].parentNode.parentNode.parentNode.parentNode;\n",
       "        var cells = IPython.notebook.get_cells();\n",
       "        for (var i = 0; i < cells.length; i++) {\n",
       "            if (mydiv === cells[i].element[0]) {\n",
       "                return i;\n",
       "            }\n",
       "        }\n",
       "        return null;\n",
       "    };\n",
       "\n",
       "    document.jigsaw_clear_output = function (workspace_filename) {\n",
       "        var workspace = document.jigsaw_workspaces[workspace_filename];\n",
       "        var cell_index = document.jigsaw_get_cell(workspace.element);\n",
       "        var cell = IPython.notebook.get_cell(cell_index);\n",
       "        // FIXME: brittle and ugly:\n",
       "        cell.element[0].children[2].children[1].children[2].children[1].children[0].innerHTML = \"\"\n",
       "        cell.output_area.outputs[2].text = \"\"\n",
       "    };\n",
       "\n",
       "    try {\n",
       "        document.element = element;\n",
       "    } catch (err) {\n",
       "        // rendering\n",
       "    }\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<iframe src=\"workspace2.html\" width=\"100%\" height=\"350\" style=\"resize: both; overflow: auto;\"></iframe>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "output",
     "output_type": "stream",
     "text": [
      "11\n"
     ]
    }
   ],
   "source": [
    "%jigsaw Python --workspace workspace2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"workspace2.png\"></img>\n",
    "\n",
    "In the above example, I dragged the following blocks:\n",
    "    \n",
    "1. print from Text\n",
    "2. expression from Python\n",
    "3. set ... to from Variables\n",
    "4. sqrt(2) from Math\n",
    "\n",
    "I had to click on the set block and create a new variable named \"sqrt_two\". I also had to click on the math constant block to change pi to sqrt(2).\n",
    "\n",
    "If you click on the \"Generate Python Code\" it will create a new cell below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "\n",
    "print((myfunc(5, 6)))\n",
    "sqrt_two = math.sqrt(2)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you enter the variable named \"sqrt_two\" in the following cell and run it, you get as output the result from running the Jigsaw blocks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.4142135623730951"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sqrt_two"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Try it out!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.4.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
